#include "TerrainSystem.h"
#include "ETTerrainManager.h"
#include <stdio.h>
#include <fstream>
#include <ios>
#include "TextOutput.h"
#include "XMLHandler.h"
#include <string.h>

TerrainConfig terrain_config;

//OgreQueueText *OgreQueueText::s_instance = 0;

ET::TerrainManager* TerrainSystem::mTerrainMgr = NULL;
ET::SplattingManager* TerrainSystem::mSplatMgr = NULL;
const ET::TerrainInfo* TerrainSystem::mTerrainInfo = NULL;
Ogre::SceneNode* TerrainSystem::mPointer = NULL;

//bool TerrainSystem::isTerrainLoaded = false;
bool TerrainSystem::mDeform = true;

ET::Brush TerrainSystem::mEditBrush = ET::Brush();
Ogre::Image TerrainSystem::terrainBrushImage = Ogre::Image();
Ogre::Image TerrainSystem::paintBrushImage = Ogre::Image();
DecalCursor *TerrainSystem::mPincelDecal = NULL;

//string TerrainSystem::mHeightmapFileName = "";

bool TerrainSystem::enablePainting = true;
bool TerrainSystem::riseTerrain = true;
bool TerrainSystem::paintTerrain = true;
bool TerrainSystem::smoothTerrain = true;

std::string TerrainSystem::textureList[6] = {"","","","","",""};

void TerrainSystem::init() {
	
}

void TerrainSystem::setup() { 

}	

void TerrainSystem::update() { 
	paint();

}

void TerrainSystem::destroy() { 

} 

/*
unsigned int currentSplattingTextures;
std::string CursorMesh
std::string terrain_config.heightmapFileName;
unsigned int heightmapHeight;
unsigned int heightmapWidth;
float terrain_config.lightmapHeight;
float terrain_config.lightmapWidth;
unsigned int LODErrorMargin;
float LODMorphFactor;
bool LODMorphing;
unsigned int maxSplattingTextures;
unsigned int terrain_config.mChosenTexture;
unsigned short terrain_config.paintBrushHeight;
float paintBrushWidth;

std::string terrain_config.paintBrushImage;
float paintBrushIntensity;
float splatTextureHeight;
std::string splatTextureWidth;
std::string splatTextureName
float terrainBrushHeight;
unsigned short terrainBrushWidth
float terrainSizeX
float terrainSizeY
float terrainSizeZ
*/

TerrainSystem::TerrainSystem() {
	resetParameters();
}

TerrainSystem::~TerrainSystem() {
	delete mTerrainMgr;
	delete mSplatMgr;
	
	Ogre::ResourcePtr rm = TextureManager::getSingleton().getByName("ETLightmap");
	TextureManager::getSingleton().remove(rm);
}
void TerrainSystem::addTexture(const std::string name) {

		//if(terrain_config.
	
	if(!terrain_config.mIsTerrainLoaded) {
		//OgreQueueText::instance()->
		OgreQueueText::instance()->Add("Create the terrain first");
		//OgreQueueText::instance()
		return;
	}

	//unsigned int maxTextures = static_cast<TerrainSystem*>(GameSystemManager::getSingleton()->getSystem("TerrainSystem"))->getParam("maxSplattingTextures")->toUInt();
	//unsigned int currTextures = static_cast<TerrainSystem*>(GameSystemManager::getSingleton()->getSystem("TerrainSystem"))->getParam("currentSplattingTextures")->toUInt();
	
	/*
	GameSystemParameter *gpMaxTextures = static_cast<GameSystemParameter*>(GameSystemManager::getSingleton()
										->getSystem("TerrainSystem")->getParam("maxSplattingTextures"));

	GameSystemParameter *gpCurrTextures = static_cast<GameSystemParameter*>(GameSystemManager::getSingleton()
										->getSystem("TerrainSystem")->getParam("currentSplattingTextures"));
	*/

	//gpMaxTextures

	if(terrain_config.mMaxSplattingTextures==terrain_config.mCurrentSplattingTextures)
		return;

	//unsigned int currTextures;
	//currTextures = *mCurrentSplattingTextures;

	FileInfoListPtr finfo =  ResourceGroupManager::getSingleton().findResourceFileInfo("ET", name);
	FileInfoList::iterator it = finfo->begin();
	if (it != finfo->end())
	{
		(terrain_config.mCurrentSplattingTextures)++;
		//gpCurrTextures->setValue(currTextures);

		Ogre::String completeFileName ((it)->archive->getName() + "/" + name);

		for(unsigned int i = 0; i < terrain_config.mMaxSplattingTextures;i++) {
			if(textureList[i] == "") {
				Ogre::String newName = "splatting" + Ogre::StringConverter::toString(i) + ".png";
				Ogre::String outfileName ((it)->archive->getName() + "/" + newName);
				textureList[i] = newName;
				CopyFile((LPCSTR)completeFileName.c_str(),(LPCSTR)outfileName.c_str(),0);
				return;
			}
		}
	}
}
void TerrainSystem::removeTexture(const std::string name) {

	/*
	GameSystemParameter *gpMaxTextures = static_cast<GameSystemParameter*>(GameSystemManager::getSingleton()
		->getSystem("TerrainSystem")->getParam("maxSplattingTextures"));

	GameSystemParameter *gpCurrTextures = static_cast<GameSystemParameter*>(GameSystemManager::getSingleton()
		->getSystem("TerrainSystem")->getParam("currentSplattingTextures"));

	*/

	if(terrain_config.mCurrentSplattingTextures==0)
		return;

	//unsigned int currTextures = mCurrentSplattingTextures;

	FileInfoListPtr finfo =  ResourceGroupManager::getSingleton().findResourceFileInfo("ET", name);
	FileInfoList::iterator it = finfo->begin();
	if (it != finfo->end())
	{
		Ogre::String completeFileName ((it)->archive->getName() + "/" + name); 
		for(unsigned int i = 0; i<terrain_config.mMaxSplattingTextures;i++) {
			std::string currTexture = textureList[i];
			if(currTexture == name) {
 				textureList[i] = "";
				//currTextures--;
				(terrain_config.mCurrentSplattingTextures)--;

				remove(completeFileName.c_str());
			}
		}
	}
}

void TerrainSystem::searchDiskForTextures() {

	/*
	GameSystemParameter *gpMaxTextures = static_cast<GameSystemParameter*>(GameSystemManager::getSingleton()
		->getSystem("TerrainSystem")->getParam("maxSplattingTextures"));

	GameSystemParameter *gpCurrTextures = static_cast<GameSystemParameter*>(GameSystemManager::getSingleton()
		->getSystem("TerrainSystem")->getParam("currentSplattingTextures"));
	*/

	terrain_config.mCurrentSplattingTextures = 0;
	//unsigned int currentTextures = 0;

	StringVectorPtr resourceNames = ResourceGroupManager::getSingleton().findResourceNames("ET","splatting*.png" );
	std::vector<Ogre::String>::const_iterator itResourceName = resourceNames->begin();
	while ( itResourceName != resourceNames->end() )
	{
		if(terrain_config.mCurrentSplattingTextures < terrain_config.mMaxSplattingTextures) {
			for(unsigned int i = 0; i < terrain_config.mMaxSplattingTextures;i++) {
				std::string texNum = (*itResourceName).substr(9,1);

				std::string r = (*itResourceName).substr(10,5);
				
				//if(r=="_norm")
				//	break;
				
				//std::string texNum = itResourceName.substr(9,1);
				int itmp = terrain_config.mCurrentSplattingTextures;
				
				textureList[terrain_config.mCurrentSplattingTextures] = "splatting" + texNum + ".png";
				//currentTextures++;
				//gpCurrTextures->setValue(currentTextures);
				terrain_config.mCurrentSplattingTextures++;
				break;
			}
		}
		++itResourceName;
	} 	
}


//TerrainSystem::isTerrainLoaded() {
//	
//}

void TerrainSystem::resetParameters()
{
	terrain_config.mIsTerrainLoaded = false;
	terrain_config.mHeightmapFileName == "";
	terrain_config.mTerrainSizeX = 1000;
	terrain_config.mTerrainSizeY = 300;
	terrain_config.mTerrainSizeZ = 1000;
	terrain_config.mLODMorphFactor = 0.2f;
	terrain_config.mLODMorphing = true;
	terrain_config.mLODErrorMargin = 2.0f;
	terrain_config.mMaxSplattingTextures = 6;
	terrain_config.mCurrentSplattingTextures = 0;

}

void TerrainSystem::generateTerrain() {

	if(terrain_config.mIsTerrainLoaded)
		return;

	MaterialManager::getSingleton().unloadAll();
	MaterialManager::getSingleton().reloadAll();

	searchDiskForTextures();

	terrain_config.mIsTerrainLoaded = true;

	SceneManager *mSceneMgr = GameSceneManager::getSingleton()->getSceneManager();
	Camera *mCamera = GameSceneManager::getSingleton()->getCamera();

	// create terrain manager
	mTerrainMgr = new ET::TerrainManager(mSceneMgr);


	//if(terrain_config.mHeightmapFileName == "")
	if(GameSceneManager::getSingleton()->currentMap == "")
		terrain_config.mHeightmapFileName = "ETterrain000.png";

	mTerrainMgr->setLODErrorMargin(terrain_config.mLODErrorMargin
									, mCamera->getViewport()->getActualHeight());
	
	mTerrainMgr->setUseLODMorphing(terrain_config.mLODMorphing
									,terrain_config.mLODMorphFactor, "morphFactor");
	
	// create a fresh, mid-high terrain for editing
	ET::TerrainInfo terrainInfo(terrain_config.mHeightmapWidth, terrain_config.mHeightmapHeight
								, vector<float>((terrain_config.mHeightmapWidth) * (terrain_config.mHeightmapHeight), 0.5f));

	// set position and size of the terrain
	terrainInfo.setExtents(AxisAlignedBox(0, 0, 0, terrain_config.mTerrainSizeX, terrain_config.mTerrainSizeY, terrain_config.mTerrainSizeZ));

	// now render it
	mTerrainMgr->destroyTerrain();
	mTerrainMgr->createTerrain(terrainInfo,33,255,true,true);

	mSplatMgr = new ET::SplattingManager("ETSplatting", "ET", 128, 128, 3);
	// specify number of splatting textures we need to handle
	mSplatMgr->setNumTextures(terrain_config.mMaxSplattingTextures);
	
	// create a manual lightmap texture
	Ogre::TexturePtr alightmapTex = TextureManager::getSingleton().createManual(
	"ETLightmap", "ET", TEX_TYPE_2D, 128, 128, 1, PF_BYTE_RGB);
	Ogre::Image lightmap;
	ET::createTerrainLightmap(terrainInfo, lightmap, 128, 128, Ogre::Vector3(1, -1, 1), ColourValue::White,
	Ogre::ColourValue(0.3, 0.3, 0.3));
	alightmapTex->getBuffer(0, 0)->blitFromMemory(lightmap.getPixelBox(0, 0));

	// load the terrain material and assign it
	MaterialPtr material (MaterialManager::getSingleton().getByName("ETTerrainMaterial"));
	mTerrainMgr->setMaterial(material);
	
	Ogre::Image image;
	
	//std::string t2 = GameSystemManager::getSingleton()->getTerrain()->mHeightmapFileName;
	
	std::string t1 = terrain_config.mHeightmapFileName;


	if(GameSceneManager::getSingleton()->currentMap != "")
		image.load("../../Media/Maps/" + terrain_config.mHeightmapFileName, "ET");
	else
		image.load("ETterrain000.png", "ET");

	//image.load("terrain_ETM.png", "ET");

	ET::TerrainInfo info;
	ET::loadHeightmapFromImage(info, image);
	//info.setExtents(AxisAlignedBox(0, 0, 0, 1500, 300, 1500));
	info.setExtents(AxisAlignedBox(0, 0, 0, terrain_config.mTerrainSizeX, terrain_config.mTerrainSizeY, terrain_config.mTerrainSizeZ));
	mTerrainMgr->createTerrain(info,33,255,true,true);
	mTerrainInfo = & mTerrainMgr->getTerrainInfo();
	
	// now load the splatting maps
	for (Ogre::uint i = 0; i < mSplatMgr->getNumMaps(); ++i)
	{
		if(GameSceneManager::getSingleton()->currentMap != "")
			image.load("../../Media/Maps/" + GameSceneManager::getSingleton()->currentMapName + "_ETcoverage."+StringConverter::toString(i)+".png", "ET");
		else
			image.load("ETcoverage."+StringConverter::toString(i)+".png", "ET");

		mSplatMgr->loadMapFromImage(i, image);
	}

	// update the lightmap

	//updateLightmap();

	//Entity* pointer = GameSceneManager::getSingleton()->getSceneManager()->createEntity("Pointer", "arrow.mesh");
	mPointer = GameSceneManager::getSingleton()->getSceneManager()->getRootSceneNode()->createChildSceneNode("PointerNode");
	//mPointer->attachObject(pointer);


	
	mPincelDecal = new DecalCursor(GameSceneManager::getSingleton()->getSceneManager()
									,material,Vector2(64,64),"decal2.png");
	//, mTerrainMgr

	setTerrainEditBrush(5);
	//mChosenTexture = 1;

	GameSystemManager::getSingleton()->createPaging();
}

/*

terrain->SetAttribute("mTerrainBrushHeight", "12");
terrain->SetAttribute("mTerrainBrushIntensity", "2.0f");
terrain->SetAttribute("mSplatTextureName", "asdasd.png");
terrain->SetAttribute("mSplatTextureHeight", "2.0f");
terrain->SetAttribute("mSplatTextureWidth", "2.0f");
terrain->SetAttribute("mMaxSplattingTextures", "1");
terrain->SetAttribute("mCurrentSplattingTextures", "1");
terrain->SetAttribute("mChosenTexture", "1");
terrain->SetAttribute("mPaintBrushImage", "adasd.png");
terrain->SetAttribute("mPaintBrushWidth", "20");
terrain->SetAttribute("mPaintBrushHeight", "20");
terrain->SetAttribute("mPaintBrushIntensity", "2");
terrain->SetAttribute("mLightmapHeight", "20.0f");
terrain->SetAttribute("mLightmapWidth", "20.0f");
terrain->SetAttribute("mLODErrorMargin", "1");
terrain->SetAttribute("mLODMorphing", "true");
terrain->SetAttribute("mLODMorphFactor", "2.0f");
terrain->SetAttribute("mCursorMesh", "asdas.png");
*/


void TerrainSystem::setAttribute(std::string name,std::string  value)
{
	//std::string a;
	//a = name;

	//if(name == "mIsTerrainLoaded")
		//terrain_config.mIsTerrainLoaded = Ogre::StringConverter::parseBool(value);
	if(name == "mTerrainSizeX")
		terrain_config.mTerrainSizeX = Ogre::StringConverter::parseReal(value);
	if(name == "mTerrainSizeY")
		terrain_config.mTerrainSizeY = Ogre::StringConverter::parseReal(value);
	if(name == "mTerrainSizeZ")
		terrain_config.mTerrainSizeZ = Ogre::StringConverter::parseReal(value);
	if(name == "mHeightmapFileName")
		terrain_config.mHeightmapFileName = value;
	if(name == "mHeightmapHeight")
		terrain_config.mHeightmapHeight = Ogre::StringConverter::parseUnsignedInt(value);
	if(name == "mHeightmapWidth")
		terrain_config.mHeightmapWidth = Ogre::StringConverter::parseUnsignedInt(value);
	if(name == "mTerrainBrushImage")
		terrain_config.mTerrainBrushImage = value;
	if(name == "mTerrainBrushWidth")
		terrain_config.mTerrainBrushWidth = Ogre::StringConverter::parseUnsignedInt(value);
	if(name == "mTerrainBrushHeight")
		terrain_config.mTerrainBrushHeight = Ogre::StringConverter::parseUnsignedInt(value);
	if(name == "mTerrainBrushIntensity")
		terrain_config.mTerrainBrushIntensity = Ogre::StringConverter::parseReal(value);
	if(name == "mSplatTextureName")
		terrain_config.mSplatTextureName = value;
	if(name == "mSplatTextureHeight")
		terrain_config.mSplatTextureHeight = Ogre::StringConverter::parseReal(value);
	if(name == "mSplatTextureWidth")
		terrain_config.mSplatTextureWidth = Ogre::StringConverter::parseReal(value);
	if(name == "mMaxSplattingTextures")
		terrain_config.mMaxSplattingTextures = Ogre::StringConverter::parseUnsignedInt(value);
	if(name == "mCurrentSplattingTextures")
		terrain_config.mCurrentSplattingTextures = Ogre::StringConverter::parseUnsignedInt(value);
	if(name == "mChosenTexture")
		terrain_config.mChosenTexture = Ogre::StringConverter::parseUnsignedInt(value);
	if(name == "mPaintBrushImage")
		terrain_config.mPaintBrushImage = value;
	if(name == "mPaintBrushWidth")
		terrain_config.mPaintBrushWidth = Ogre::StringConverter::parseUnsignedInt(value);
	if(name == "mPaintBrushHeight")
		terrain_config.mPaintBrushHeight = Ogre::StringConverter::parseUnsignedInt(value);
	if(name == "mPaintBrushIntensity")
		terrain_config.mPaintBrushIntensity = Ogre::StringConverter::parseReal(value);
	if(name == "mLightmapHeight")
		terrain_config.mLightmapHeight = Ogre::StringConverter::parseReal(value);
	if(name == "mLightmapWidth")
		terrain_config.mLightmapWidth = Ogre::StringConverter::parseReal(value);
	if(name == "mLODErrorMargin")
		terrain_config.mLODErrorMargin = Ogre::StringConverter::parseUnsignedInt(value);
	if(name == "mLODMorphing")
		terrain_config.mLODMorphing = Ogre::StringConverter::parseBool(value);
	if(name == "mLODMorphFactor")
		terrain_config.mLODMorphFactor = Ogre::StringConverter::parseReal(value);
	if(name == "mCursorMesh")
		terrain_config.mCursorMesh = value;
}


void TerrainSystem::saveAttrib(TiXmlElement* mElem)
{
	mElem->SetAttribute("mIsTerrainLoaded","false");
	mElem->SetAttribute("mTerrainSizeX",Ogre::StringConverter::toString(terrain_config.mTerrainSizeX).c_str());
	mElem->SetAttribute("mTerrainSizeY",Ogre::StringConverter::toString(terrain_config.mTerrainSizeY).c_str());
	mElem->SetAttribute("mTerrainSizeZ",Ogre::StringConverter::toString(terrain_config.mTerrainSizeZ).c_str());
	mElem->SetAttribute("mHeightmapFileName",terrain_config.mHeightmapFileName.c_str());
	mElem->SetAttribute("mHeightmapHeight",Ogre::StringConverter::toString(terrain_config.mHeightmapHeight).c_str());
	mElem->SetAttribute("mHeightmapWidth",Ogre::StringConverter::toString(terrain_config.mHeightmapWidth).c_str());
	mElem->SetAttribute("mTerrainBrushImage",terrain_config.mTerrainBrushImage.c_str());
	mElem->SetAttribute("mTerrainBrushWidth",Ogre::StringConverter::toString(terrain_config.mTerrainBrushWidth).c_str());
	mElem->SetAttribute("mTerrainBrushHeight",Ogre::StringConverter::toString(terrain_config.mTerrainBrushHeight).c_str());
	mElem->SetAttribute("mTerrainBrushIntensity",Ogre::StringConverter::toString(terrain_config.mTerrainBrushIntensity).c_str());
	mElem->SetAttribute("mSplatTextureName",terrain_config.mSplatTextureName.c_str());
	mElem->SetAttribute("mSplatTextureHeight",Ogre::StringConverter::toString(terrain_config.mSplatTextureHeight).c_str());
	mElem->SetAttribute("mSplatTextureWidth",Ogre::StringConverter::toString(terrain_config.mSplatTextureWidth).c_str());
	mElem->SetAttribute("mMaxSplattingTextures",Ogre::StringConverter::toString(terrain_config.mMaxSplattingTextures).c_str());
	mElem->SetAttribute("mCurrentSplattingTextures",Ogre::StringConverter::toString(terrain_config.mCurrentSplattingTextures).c_str());
	mElem->SetAttribute("mChosenTexture",Ogre::StringConverter::toString(terrain_config.mChosenTexture).c_str());
	mElem->SetAttribute("mPaintBrushImage",terrain_config.mPaintBrushImage.c_str());
	mElem->SetAttribute("mPaintBrushWidth",Ogre::StringConverter::toString(terrain_config.mPaintBrushWidth).c_str());
	mElem->SetAttribute("mPaintBrushHeight",Ogre::StringConverter::toString(terrain_config.mPaintBrushHeight).c_str());
	mElem->SetAttribute("mPaintBrushIntensity",Ogre::StringConverter::toString(terrain_config.mPaintBrushIntensity).c_str());
	mElem->SetAttribute("mLightmapHeight",Ogre::StringConverter::toString(terrain_config.mLightmapHeight).c_str());
	mElem->SetAttribute("mLightmapWidth",Ogre::StringConverter::toString(terrain_config.mLightmapWidth).c_str());
	mElem->SetAttribute("mLODErrorMargin",Ogre::StringConverter::toString(terrain_config.mLODErrorMargin).c_str());
	mElem->SetAttribute("mLODMorphing",Ogre::StringConverter::toString(terrain_config.mLODMorphing).c_str());
	mElem->SetAttribute("mLODMorphing",Ogre::StringConverter::toString(terrain_config.mLODMorphing).c_str());
	mElem->SetAttribute("mLODMorphFactor",Ogre::StringConverter::toString(terrain_config.mLODMorphFactor).c_str());
	mElem->SetAttribute("mCursorMesh",terrain_config.mCursorMesh.c_str());
}

/*
void TerrainSystem::saveSettings(std::string mFileName) //TiXmlElement* element
{
	TiXmlElement* mTerrElem;
	TiXmlHandle hRoot(0);
	TiXmlAttribute* pAttrib; 
	TiXmlDocument doc(mFileName.c_str());
	TiXmlHandle hDoc(&doc);
	TiXmlElement* mRootObject;
	
	saveTerrain(mFileName);
	
	TiXmlDeclaration* decl = new TiXmlDeclaration( "1.0", "", "" );  
	doc.LinkEndChild(decl);
	
	mRootObject = new TiXmlElement( "ApeEngine" );
	doc.LinkEndChild( mRootObject );
	
	TiXmlComment * comment = new TiXmlComment();
	comment->SetValue("Ape Engine map file :)" );  
	mRootObject->LinkEndChild( comment );  
	
	if (!mRootObject) 
		return;
	
	mTerrElem = new TiXmlElement("Terrain");  
	mRootObject->LinkEndChild(mTerrElem);
	
	//create if not already
	if(mTerrElem)
			saveAttrib(mTerrElem);
	
	mFileName += ".xml";
	
	std::string mPath = "../../Media/Maps/" + mFileName;
	
	doc.SaveFile(mPath.c_str()); // + mFileName.c_str()

}
*/

void TerrainSystem::updateLightmap()
{
	if(!terrain_config.mIsTerrainLoaded)
		return;

	Image lightmap;
	ET::createTerrainLightmap(*mTerrainInfo, lightmap, 128, 128, Vector3(1, -1, 1), ColourValue(1,1,1),
	ColourValue(0.3, 0.3,  0.3));

	// get our dynamic texture and update its contents
	TexturePtr tex = TextureManager::getSingleton().getByName("ETLightmap");
	tex->getBuffer(0, 0)->blitFromMemory(lightmap.getPixelBox(0, 0));
}

Ogre::Real TerrainSystem::getTerrainHeightAt(Real x,Real z) {
	
	Ray ray;
	ray.setOrigin(Vector3(x, 0.0f, z));
	ray.setDirection(Vector3::UNIT_Y);

	std::pair<bool, Vector3> result = mTerrainInfo->rayIntersects(ray);
	if (result.first)
	{
		return result.second.y;
	}
	//LogManager::getSingleton().logMessage("No hay resultados para: X = " + StringConverter::toString(result.second.x) + " Y = " + StringConverter::toString(result.second.y) + " Z = " + StringConverter::toString(result.second.z) ); 
	return 0.0f;
}

Ogre::Vector3 TerrainSystem::rayIntersectsTerrain() {
	int width = GameSystemManager::getSingleton()->getInput()->getMouseStat().width;
	int height = GameSystemManager::getSingleton()->getInput()->getMouseStat().height;
	int Xabs = GameSystemManager::getSingleton()->getInput()->getMouseStat().X.abs;
	int Yabs = GameSystemManager::getSingleton()->getInput()->getMouseStat().Y.abs;
	
	Ray mouseRay = GameSceneManager::getSingleton()->getCamera()->getCameraToViewportRay(Xabs/float(width), Yabs/float(height));
	std::pair<bool, Vector3> result = mTerrainInfo->rayIntersects(mouseRay);
	if (result.first)
	{
		return result.second;
	}
	return Vector3::ZERO;
}

void TerrainSystem::updateBrush(const OIS::MouseEvent &evt) { //Ogre::Real screenX,Ogre::Real screenY
	// Update CEGUI with the mouse motion
	//CEGUI::System::getSingleton().injectMouseMove( arg.state.X.rel, arg.state.Y.rel );
	if(!terrain_config.mIsTerrainLoaded)
		return;
	
	//if(!GameSystemManager::getSingleton()->getInput()->isLeftButtonDown())
	//	return;

	// whenever the mouse is moved, we update the position on the
	// terrain to which the mouse is currently pointing
	int width = GameSystemManager::getSingleton()->getInput()->getMouseStat().width;
	int height = GameSystemManager::getSingleton()->getInput()->getMouseStat().height;
	int Xabs = GameSystemManager::getSingleton()->getInput()->getMouseStat().X.abs;
	int Yabs = GameSystemManager::getSingleton()->getInput()->getMouseStat().Y.abs;

	//evt.state.X.abs/float(evt.state.width)
	//evt.state.Y.abs/float(evt.state.height)

	Ray mouseRay = GameSceneManager::getSingleton()->getCamera()->getCameraToViewportRay(evt.state.X.abs/float(evt.state.width), evt.state.Y.abs/float(evt.state.height));
	//Ray mouseRay = GameSceneManager::getSingleton()->getCamera()->getCameraToViewportRay(Xabs/float(width), Yabs/float(height));
	// since ETM is no longer a scene manager, we don't use a ray scene query,
	// but instead query the terrain info directly
	std::pair<bool, Vector3> result = mTerrainInfo->rayIntersects(mouseRay);
	if (result.first)
	{
	// update pointer's position
		mPointer->setPosition(result.second);
	}

	// If we are dragging the middle mouse button.
	//if ( mMMouseDown )
	//{
	//  mCamera->yaw( Degree(-arg.state.X.rel * mRotateSpeed) );
	//  mCamera->pitch( Degree(-arg.state.Y.rel * mRotateSpeed) );
	//}
}

void TerrainSystem::paint() {

	if(!terrain_config.mIsTerrainLoaded || !enablePainting)
		return;

	
	if(GameSceneManager::getSingleton()->getBrush() == BRUSH_PAINT || GameSceneManager::getSingleton()->getBrush() == BRUSH_DEFORM || GameSceneManager::getSingleton()->getBrush() == BRUSH_FOLIAGE && terrain_config.mIsTerrainLoaded) {
		mPincelDecal->setPosition(mPointer->getPosition());
		mPincelDecal->show();
	} else {
		mPincelDecal->hide();
	}
	

	if (GameSystemManager::getSingleton()->getInput()->isLeftButtonDown() && terrain_config.mIsTerrainLoaded) // || mRMouseDown
	{
		Real time = GameSystemManager::getSingleton()->getTime();
		if(GameSceneManager::getSingleton()->getBrush() == BRUSH_DEFORM)
		{
			//GameSystemParameter *gpIntensity = static_cast<GameSystemParameter*>(GameSystemManager::getSingleton()
			//	->getSystem("TerrainSystem")->getParam("terrainBrushIntensity"));

			if(!smoothTerrain) {
				float brushIntensity = time * (terrain_config.mTerrainBrushIntensity) * (riseTerrain ? 1 : -1); //0.4
				Vector3 deformPos = mPointer->getPosition();
				int x = mTerrainInfo->posToVertexX(deformPos.x);
				int z = mTerrainInfo->posToVertexZ(deformPos.z);
				mTerrainMgr->deform(x, z, mEditBrush, brushIntensity);
			} else {
				float brushIntensity = time * (terrain_config.mTerrainBrushIntensity) * 4; //0.4
				Vector3 deformPos = mPointer->getPosition();
				int x = mTerrainInfo->posToVertexX(deformPos.x);
				int z = mTerrainInfo->posToVertexZ(deformPos.z);
				mTerrainMgr->setHeights(x,z,averageFilter(x,z,mEditBrush,brushIntensity));
				mTerrainMgr->deform(x, z, mEditBrush, 0);
			}
		}
		if(GameSceneManager::getSingleton()->getBrush() == BRUSH_PAINT)
		{
			/*
			GameSystemParameter *gpIntensity = static_cast<GameSystemParameter*>(GameSystemManager::getSingleton()
				->getSystem("TerrainSystem")->getParam("paintBrushIntensity"));
			GameSystemParameter *gpSelectedTexture = static_cast<GameSystemParameter*>(GameSystemManager::getSingleton()
				->getSystem("TerrainSystem")->getParam("mChosenTexture"));
			*/

			float brushIntensity = time * (terrain_config.mPaintBrushIntensity) * (paintTerrain? 1 : 2) * (paintTerrain? 1 : -1); //5.0
			Vector3 paintPos = mPointer->getPosition();
			int x = mTerrainInfo->posToVertexX(paintPos.x);
			int z = mTerrainInfo->posToVertexZ(paintPos.z);
			mSplatMgr->paint(terrain_config.mChosenTexture, x, z, mEditBrush, brushIntensity);
		}
	}
}

ET::Brush TerrainSystem::averageFilter(int x, int z, ET::Brush shape, float fIntensity)
{
      // When you're doing a loop possibly thousands of times, it's worth setting these
      // aside rather than calling a function every iteration.
   size_t iWidth = shape.getWidth();
   size_t iHeight = shape.getHeight();

      // Faster to presize than resize
   std::vector<float> vecReturnBuffer(iWidth * iHeight);
   std::vector<float> vecHeightBuffer(iWidth * iHeight);
   ET::Brush brushReturn = ET::Brush(vecReturnBuffer, iWidth, iHeight);
   ET::Brush brushHeights = ET::Brush(vecHeightBuffer, iWidth, iHeight);

   mTerrainMgr->getHeights(x, z, brushHeights);

   float fSumHeights = 0.0f;
   size_t iNumHeights = iWidth * iHeight;

      // Find the sum of all the heights within the sample
   for (Ogre::uint i = 0; i < iWidth; i++)
   {
      for(Ogre::uint j = 0; j < iHeight; j++)
      {
         fSumHeights += brushHeights.at(i, j);
      }
   }

      // Find the average height within the sample
   float fAvgHeight = fSumHeights / iNumHeights;

   for (Ogre::uint i = 0; i < iWidth; i++)
   {
      for(Ogre::uint j = 0; j < iHeight; j++)
      {
         float fHeight = brushHeights.at(i, j);
         float fDelta = fHeight - fAvgHeight;
         float fShapeMask = shape.at(i, j);

         fDelta = (fDelta * fShapeMask * fIntensity);

         brushReturn.at(i, j) = fHeight - fDelta;
      }
   }

   return brushReturn;
}

void TerrainSystem::setMapSize(Ogre::Vector3 terrainSize) {
	terrain_config.mTerrainSizeX = terrainSize.x;
	terrain_config.mTerrainSizeY = terrainSize.y;
	terrain_config.mTerrainSizeZ = terrainSize.z;

	//GameSystemManager::getSingleton()->getSystem("TerrainSystem")->getParam("terrainSizeX")->setValue(terrainSize.x);
	//GameSystemManager::getSingleton()->getSystem("TerrainSystem")->getParam("terrainSizeY")->setValue(terrainSize.y);
	//GameSystemManager::getSingleton()->getSystem("TerrainSystem")->getParam("terrainSizeZ")->setValue(terrainSize.z);

}

ET::TerrainManager* TerrainSystem::getTerrainManager(){
	return mTerrainMgr;
}

void TerrainSystem::removeTerrain() {
	if(!terrain_config.mIsTerrainLoaded)
	return;

	if(mSplatMgr)
		mSplatMgr->~SplattingManager();
	
	if(mTerrainMgr)
		mTerrainMgr->~TerrainManager();

	//SceneNode *pn = GameSceneManager::getSingleton()->getSceneManager()->getSceneNode("PointerNode");
	//pn->detachAllObjects();

	GameSceneManager::getSingleton()->getSceneManager()->destroySceneNode("PointerNode");

	//GameSceneManager::getSingleton()->getSceneManager()->destroyMovableObject((MovableObject*)pn);// getSceneNode("PointerNode")->detachAllObjects();// getRootSceneNode()->detachObject("PointerNode");
	GameSceneManager::getSingleton()->getSceneManager()->destroyEntity("Pointer");// getEntity("Pointer")-> _deinitialise();// detachObject("PointerNode");
	//Ogre::ResourcePtr rm = Ogre::ResourceManager::getByName("Pointer"); //TextureManager::getSingleton().getByName("ETLightmap");
	//Ogre::ResourceManager::remove(rm);

	//if(!lightmapTex.isNull()) {
		Ogre::ResourcePtr rm = TextureManager::getSingleton().getByName("ETLightmap");
		TextureManager::getSingleton().remove(rm);
	//}
	//if(mTerrainMgr)
	//	mTerrainMgr->~SplattingManager();
		
	terrain_config.mIsTerrainLoaded = false;
}

//void TerrainSystem::setBrushSize()
//{
//	mEditBrush
//}
void TerrainSystem::setTerrainEditBrushIntensity(float size)
{
	//terrain_config.terrainBrushIntensity = size;
	//GameSystemManager::getSingleton()->getSystem("TerrainSystem")->getParam("terrainBrushIntensity")->setValue(size);	
	terrain_config.mTerrainBrushIntensity = size;

}
void TerrainSystem::setPaintEditBrushIntensity(float size)
{
	//terrain_config.paintBrushIntensity = size;
	//GameSystemManager::getSingleton()->getSystem("TerrainSystem")->getParam("paintBrushIntensity")->setValue(size);	
	terrain_config.mPaintBrushIntensity = size;
}

void TerrainSystem::setTerrainEditBrush(unsigned short size)
{
	if(size==0)
		return;

	//GameSystemManager::getSingleton()->getSystem("TerrainSystem")->getParam("terrainBrushHeight")->setValue(size);	
	//GameSystemManager::getSingleton()->getSystem("TerrainSystem")->getParam("terrainBrushWidth")->setValue(size);	

	terrain_config.mTerrainBrushHeight = (unsigned int)size;
	terrain_config.mTerrainBrushWidth = (unsigned int)size;

	if(mPincelDecal)
		mPincelDecal->setSize(Vector2(size * 10,size * 10));

	terrainBrushImage.load("brush.png", "ET");
	terrainBrushImage.resize(size,size);
	mEditBrush = ET::loadBrushFromImage(terrainBrushImage);
}
void TerrainSystem::setPaintEditBrush(unsigned short size)
{
	if(size==0)
		return;

	//GameSystemManager::getSingleton()->getSystem("TerrainSystem")->getParam("paintBrushHeight")->setValue(size);	
	//GameSystemManager::getSingleton()->getSystem("TerrainSystem")->getParam("paintBrushWidth")->setValue(size);	

	terrain_config.mPaintBrushHeight = size;
	terrain_config.mPaintBrushWidth = size;

	if(mPincelDecal)
		mPincelDecal->setSize(Vector2(size * 7,size * 7));

	paintBrushImage.load("brush.png", "ET");
	paintBrushImage.resize(size,size);
	mEditBrush = ET::loadBrushFromImage(paintBrushImage);
}

void TerrainSystem::mouseButtonDownNotification(bool controledByGui,unsigned int button) {
	if(button == OIS::MB_Left) {
		if(controledByGui)
			enablePainting = false; //GameSystemManager::getSingleton()->getTerrain()->
		else
			enablePainting = true; //GameSystemManager::getSingleton()->getTerrain()->
	}
}
void TerrainSystem::mouseButtonUpNotification(bool controledByGui,unsigned int button) {
	if(button == OIS::MB_Left) {
		//if(GameSceneManager::getSingleton()->getBrush() == BrushType::BRUSH_DEFORM)
			//updateLightmap(); //GameSystemManager::getSingleton()->getTerrain()->
	}
}

void TerrainSystem::saveTerrain(std::string mFileName)
{
	// just for demonstration, we'll save anything we can
	// start with the terrain
	Image image;
	ET::saveHeightmapToImage(*mTerrainInfo, image);
	//image.save("ETterrain.png");
	
	terrain_config.mHeightmapFileName = mFileName + ".png";

	//ResourceGroupManager::getSingleton().getResourceGroup()

	image.save("../../Media/Maps/" + terrain_config.mHeightmapFileName);

	// now save the splatting maps
	for (uint i = 0; i < mSplatMgr->getNumMaps(); ++i)
	{
		mSplatMgr->saveMapToImage(i, image);
		image.save("../../Media/Maps/" + mFileName + "_ETcoverage."+StringConverter::toString(i)+".png");
	}

	// now the lightmap
	Image lightmap;
	ET::createTerrainLightmap(*mTerrainInfo, lightmap, 512, 512, Vector3(1, -1, 1), ColourValue(1,1,1),
	ColourValue(0.3, 0.3,  0.3));
	lightmap.save("../../Media/Maps/" + mFileName + "_ETlightmap.png");

	// generate a base texture for this terrain (could be used for older hardware instead of splatting)
	// create an ImageList of our splatting textures.
	ET::ImageList textures (6);
	
	for (int i = 0; i < 6; ++i)
		textures[i].load("splatting"+StringConverter::toString(i)+".png", "ET");

	// create the base texture
	Image baseTexture;
	mSplatMgr->createBaseTexture(baseTexture, 512, 512, textures, 20, 20);
	baseTexture.save("../../Media/Maps/" + mFileName + "_ETbase.png");

	// finally create a minimap using the lightmap and the generated base texture
	Image minimap = ET::createMinimap(baseTexture, lightmap);
	minimap.save("../../Media/Maps/" + mFileName + "_ETminimap.png");

	//lightmap.save("../../Media/Maps/" + mFileName + "_ETlightmap.png");
}
/*
bool TerrainSystem::saveToXML() {
	
	saveTerrain();

	XMLHandler *xml = GameSystemManager::getSingleton()->getXMLHandler();

	TiXmlElement* parent = xml->createParentElement("TerrainConfig");
	
	TiXmlElement* wnd = xml->createChildElement(parent,"TerrainInfo");
	wnd->SetAttribute("Name", "ETM");
	return true;
}
*/